<!DOCTYPE html>


<html lang="zh-CN">


<head>
  <meta charset="utf-8" />
    
  <meta name="description" content="迎着朝阳的博客" />
  
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
  <title>
    zookeeper使用教程 |  迎着朝阳
  </title>
  <meta name="generator" content="hexo-theme-ayer">
  
  <link rel="shortcut icon" href="https://dxysun.com/static/yan.png" />
  
  
<link rel="stylesheet" href="/dist/main.css">

  
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Shen-Yu/cdn/css/remixicon.min.css">

  
<link rel="stylesheet" href="/css/custom.css">

  
  
<script src="https://cdn.jsdelivr.net/npm/pace-js@1.0.2/pace.min.js"></script>

  
  

  
<script>
var _hmt = _hmt || [];
(function() {
	var hm = document.createElement("script");
	hm.src = "https://hm.baidu.com/hm.js?aa994a8d65700b8835787dd39d079d7e";
	var s = document.getElementsByTagName("script")[0]; 
	s.parentNode.insertBefore(hm, s);
})();
</script>


</head>

</html>

<body>
  <div id="app">
    
      
    <main class="content on">
      <section class="outer">
  <article
  id="post-zookeeperForUse"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h1 class="article-title sea-center" style="border-left:0" itemprop="name">
  zookeeper使用教程
</h1>
 

    </header>
     
    <div class="article-meta">
      <a href="/2021/08/28/zookeeperForUse/" class="article-date">
  <time datetime="2021-08-28T08:24:55.000Z" itemprop="datePublished">2021-08-28</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/zookeeper/">zookeeper</a>
  </div>
  
<div class="word_count">
    <span class="post-time">
        <span class="post-meta-item-icon">
            <i class="ri-quill-pen-line"></i>
            <span class="post-meta-item-text"> 字数统计:</span>
            <span class="post-count">3k</span>
        </span>
    </span>

    <span class="post-time">
        &nbsp; | &nbsp;
        <span class="post-meta-item-icon">
            <i class="ri-book-open-line"></i>
            <span class="post-meta-item-text"> 阅读时长≈</span>
            <span class="post-count">11 分钟</span>
        </span>
    </span>
</div>
 
    </div>
      
    <div class="tocbot"></div>




  
    <div class="article-entry" itemprop="articleBody">
       
  <p>zookeeper使用教程</p>
<a id="more"></a>

<h1 id="什么是-zookeeper"><a href="#什么是-zookeeper" class="headerlink" title="什么是 zookeeper"></a>什么是 zookeeper</h1><p>zookeeper 是一个开源的分布式协调服务，由雅虎公司创建，是 google chubby 的开源实现。zookeeper 的设计目<br>标是将哪些复杂且容易出错的分布式一致性服务封装起来，构成一个高效可靠的原语集（由若干条指令组成的，完成<br>一定功能的一个过程），并且以一些列简单一用的接口提供给用户使用。</p>
<h1 id="zookeeper-安装部署"><a href="#zookeeper-安装部署" class="headerlink" title="zookeeper 安装部署"></a>zookeeper 安装部署</h1><p>zookeeper 有两种运行模式：集群模式和单机模式。<br>下 载 zookeeper 安装包：<br><a href="http://apache.fayea.com/zookeeper/" target="_blank" rel="noopener">http://apache.fayea.com/zookeeper/</a><br>下载完成，通过 tar -zxvf 解压</p>
<h2 id="单机环境安装"><a href="#单机环境安装" class="headerlink" title="单机环境安装"></a>单机环境安装</h2><p>一般情况下，在开发测试环境，没有这么多资源的情况下，而且也不需要特别好的稳定性的前提下，我们可以使用单机部署；<br>初次使用zookeeper ，需要将conf目录下的zoo_sample.cfg 文件 copy 一份重命名为 zoo.cfg修改 dataDir 目录，dataDir 表示日志文件存放的路径<br>启动zookeeper即为单机环境</p>
<h2 id="集群环境安装"><a href="#集群环境安装" class="headerlink" title="集群环境安装"></a>集群环境安装</h2><h3 id="角色"><a href="#角色" class="headerlink" title="角色"></a>角色</h3><p>在 zookeeper 集群中，各个节点总共有三种角色，分别是：<br>Leader，Follower，Observer</p>
<h4 id="Leader角色"><a href="#Leader角色" class="headerlink" title="Leader角色"></a>Leader角色</h4><p>Leader服务器是整个zookeeper集群的核心，主要的工作任务有两项</p>
<p>1、事物请求的唯一调度和处理者，保证集群事物处理的顺序性</p>
<p>2、集群内部各服务器的调度者</p>
<h4 id="Follower角色"><a href="#Follower角色" class="headerlink" title="Follower角色"></a>Follower角色</h4><p>Follower角色的主要职责是</p>
<p>1、处理客户端非事务请求、转发事物请求给Leader服务器</p>
<p>2、参与事物请求Proposal的投票（需要半数以上服务器通过才能通知leader commit数据；Leader发起的提案，要求Follower投票）</p>
<p>3、参与Leader选举的投票</p>
<h4 id="Observer角色"><a href="#Observer角色" class="headerlink" title="Observer角色"></a>Observer角色</h4><p>Observer是zookeeper3.3开始引入的一个全新的服务器角色，从字面来理解，该角色充当了观察者的角色。</p>
<p>观察zookeeper集群中的最新状态变化并将这些状态变化同步到Observer服务器上。</p>
<p>Observer的工作原理与Follower角色基本一致，而它和Follower角色唯一的不同在于Observer不参与任何形式的投票，包括事务请求、Proposal的投票和Leader选举的投票。</p>
<p>简单来说，observer服务器只提供非事物请求服务，通常在于不影响集群事物处理能力的前提下提升集群非事物处理的能力</p>
<h3 id="搭建"><a href="#搭建" class="headerlink" title="搭建"></a>搭建</h3><p>通常zookeeper是由2n+1台server组成，每个server都知道彼此的存在，每个server都维护的内存状态镜像以及持久化存储的事务日志和快照。</p>
<p>对于2n+1台server，只要有n+1台（大多数）server可用，整个系统保持可用。一个zookeeper集群如果要对外提供可用的服务，那么集群中必须要有过半的机器正常工作并且彼此之间能够正常通信，基于这个特性，如果向搭建一个能够允许F台机器down掉的集群，那么就要部署<code>2*F+1</code>台服务器构成的zookeeper集群。</p>
<p>因此3台机器构成的zookeeper集群，能够在挂掉一台机器后依然正常工作。一个5台机器集群的服务，能够对2台机器怪调的情况下进行容灾。如果一台由6台服务构成的集群，同样只能挂掉2台机器。因此，5台和6台在容灾能力上并没有明显优势，反而增加了网络通信负担。</p>
<p>系统启动时，集群中的server会选举出一台server为Leader，其它的就作为follower（这里先不考虑observer角色），之所以要满足这样一个等式，是因为一个节点要成为集群中的leader，需要有超过及群众过半数的节点支持，这个涉及到leader选举算法。同时也涉及到事务请求的提交投票。</p>
<p>这里集群模式采用模拟 3 台机器来搭建 zookeeper 集群。</p>
<p>分别复制安装包到三台机器上并解压，同时 copy 一份zoo.cfg</p>
<ul>
<li>修改配置文件和端口</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">server.1&#x3D;IP1:2888:3888 #2888：访问 zookeeper 的端口；3888：重新选举 leader 的端口</span><br><span class="line">server.2&#x3D;IP2.2888:3888</span><br><span class="line">server.3&#x3D;IP3.2888:2888</span><br></pre></td></tr></table></figure>
<blockquote>
<p>server.A=B：C：D：<br>A 是一个数字，表示这个是第几号服务器；B 是这个服务器的 ip 地址；C 表示的是这个服务器与集群中的 Leader<br>服务器交换信息的端口；D 表示的是万一集群中的 Leader 服务器挂了，需要一个端口来重新进行选举，选出一个新的 Leader，而这个端口就是用来执行选举时服务器相互通信的端口。如果是伪集群的配置方式，由于 B 都是一样，所以不同的 Zookeeper 实例通信端口号不能<br>一样，所以要给它们分配不同的端口号。在集群模式下，集群中每台机器都需要感知到整个集群是由哪几台机器组成的，在配置文件中，按照格式<br><code>server.id=host:port:port</code>，每一行代表一个机器配置<code>id:</code>指的是 server ID,用来标识该机器在集群中的机器序号</p>
</blockquote>
<ul>
<li><p>新建 datadir 目录，设置 myid 在每台zookeeper机器上，我们都需要在数据目录(dataDir)<br>下创建一个 myid 文件，该文件只有一行内容，对应每台机器的 Server ID 数字；<br>比如 server.1 的 myid 文件内容就是1。必须确保每个服务器的 myid 文件中的数字不同，并且和自己所在机器的 zoo.cfg 中 server.id 的 id 值一致，id 的范围是 1~255</p>
</li>
<li><p>启动 zookeeper</p>
</li>
</ul>
<p>带 Observer 角色的集群</p>
<p>Observer：在不影响写性能的情况下扩展 zookeeper本身 zookeeper 的集群性能已经很好了，但是如果超大量的客户端访问，就势必需要增加 zookeeper 集群的服务器数量，而随着服务器的增加，zookeeper 集群的写性能就会下降；zookeeper 中 znode 的变更需要半数及以上服务器投票通过，而随着机器的增加，由于网络消耗等原因必定会导致投票成本增加，也就导致性能下降的结果</p>
<h1 id="常用命令"><a href="#常用命令" class="headerlink" title="常用命令"></a>常用命令</h1><p>启动 ZK 服务</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bin&#x2F;zkServer.sh start</span><br></pre></td></tr></table></figure>
<p>查看 ZK 服务状态</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bin&#x2F;zkServer.sh status</span><br></pre></td></tr></table></figure>
<p>停止 ZK 服务</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bin&#x2F;zkServer.sh stop</span><br></pre></td></tr></table></figure>
<p>重启 ZK 服务</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bin&#x2F;zkServer.sh restart</span><br></pre></td></tr></table></figure>
<p>连接服务器</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">zkCli.sh -timeout 0 -r -server ip:port</span><br></pre></td></tr></table></figure>

<h2 id="zkCli内常用命令"><a href="#zkCli内常用命令" class="headerlink" title="zkCli内常用命令"></a>zkCli内常用命令</h2><p><code>help</code> 帮助</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">ZooKeeper -server host:port cmd args</span><br><span class="line">        stat path [watch]    #获得节点的更新信息</span><br><span class="line">        set path data [version]  #修改节点</span><br><span class="line">        ls path [watch]</span><br><span class="line">        delquota [-n|-b] path</span><br><span class="line">        ls2 path [watch]   #ls命令和stat命令的整合</span><br><span class="line">        setAcl path acl  #设置权限</span><br><span class="line">        setquota -n|-b val path</span><br><span class="line">        history</span><br><span class="line">        redo cmdno</span><br><span class="line">        printwatches on|off</span><br><span class="line">        delete path [version]   # 删除节点</span><br><span class="line">        sync path</span><br><span class="line">        listquota path</span><br><span class="line">        rmr path</span><br><span class="line">        get path [watch]  #设置watch事件</span><br><span class="line">        create [-s] [-e] path data acl  # 创建节点</span><br><span class="line">        addauth scheme auth</span><br><span class="line">        quit</span><br><span class="line">        getAcl path   #获取某个节点的acl权限信息</span><br><span class="line">        close</span><br><span class="line">        connect host:port</span><br></pre></td></tr></table></figure>

<p><code>ls</code> 查看节点命令</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ls path</span><br></pre></td></tr></table></figure>

<p>节点除了存储数据内容以外，还存储了数据节点本身的一些状态信息，通过<code>get</code>命令可以获得状态信息的详细内容，获取内容的首行为空行说明节点内容为空</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">cZxid ：创建节点的id</span><br><span class="line">ctime ： 节点的创建时间</span><br><span class="line">mZxid ：修改节点的id</span><br><span class="line">mtime ：修改节点的时间</span><br><span class="line">pZxid ：子节点的id</span><br><span class="line">cversion : 子节点的版本</span><br><span class="line">dataVersion ： 当前节点数据的版本</span><br><span class="line">aclVersion ：权限的版本</span><br><span class="line">ephemeralOwner ：判断是否是临时节点</span><br><span class="line">dataLength ： 数据的长度</span><br><span class="line">numChildren ：子节点的数量</span><br></pre></td></tr></table></figure>

<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">get path</span><br></pre></td></tr></table></figure>

<p><code>create -e</code> 创建临时节点</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">create -e path data</span><br></pre></td></tr></table></figure>

<p><code>create -s</code> 创建顺序节点 自动累加</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">create -s path data</span><br></pre></td></tr></table></figure>

<h1 id="Zookeeper的节点特性"><a href="#Zookeeper的节点特性" class="headerlink" title="Zookeeper的节点特性"></a>Zookeeper的节点特性</h1><p>临时节点</p>
<p>持久化节点</p>
<p>有序节点（递增的序列号）</p>
<p>先有父节点，再有子节点</p>
<p>临时节点下不能存在子节点</p>
<p>同级节点下，节点名字必须是唯一</p>
<h1 id="watcher通知机制"><a href="#watcher通知机制" class="headerlink" title="watcher通知机制"></a>watcher通知机制</h1><p>zookeeper提供了分布式数据的发布/订阅功能，zookeeper允许客户端向服务端注册一个watcher监听，当服务端的一些指定事件触发了watcher，那么服务端就会向客户端发送一个事件通知。</p>
<p>zookeeper提供以下几种命令来对指定节点设置监听。</p>
<p><code>get [-s] [-w] path</code>：监听指定path节点的修改和删除事件，同样该事件也是一次性触发。</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">get -w /node</span><br><span class="line"><span class="comment">#在其他窗口执行下面命令，会触发相关事件</span></span><br><span class="line"><span class="built_in">set</span> /node 123</span><br><span class="line">delete /node</span><br></pre></td></tr></table></figure>

<p><code>ls [-s] [-W] [-R] path</code>：监控指定path的子节点的添加和删除事件。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">1s -w &#x2F;node</span><br><span class="line">#在其他窗口执行下面命令，会触发相关事件</span><br><span class="line">create &#x2F;node&#x2F;node1</span><br><span class="line">delete &#x2F;node&#x2F;node1</span><br></pre></td></tr></table></figure>


<p>注意：当前命令设置的监听是一次性的，就是说一旦触发了一次事件监听，后续的事件都不会响应，当然我们可以通过重复订阅来解决</p>
<p><code>stat [-w] path</code>：作用和get完全相同。</p>
<p><code>addWatch [-m mode] path</code> </p>
<p>addWatch的作用是针对指定节点添加事件监听，支持两种模式</p>
<ul>
<li>PERSISTENT，持久化订阅，针对当前节点的修改和删除事件，以及当前节点的子节点的删除和新增事件。</li>
<li>PERSISTENT_RECURSIVE，持久化递归订阅，在PERSISTENT的基础上，增加了子节点修改的事件触发，以及子节点的子节点的数据变化都会触发相关事件（满足递归订阅特性）</li>
</ul>
<p>可以参考<a href="https://blog.csdn.net/hohoo1990/article/details/78617336" target="_blank" rel="noopener">zookeeper 中 Watcher 通知机制的一点理解</a></p>
<h1 id="Session会话机制"><a href="#Session会话机制" class="headerlink" title="Session会话机制"></a>Session会话机制</h1><p>如图所示，表示Zookeeper的session会话状态机制。</p>
<p>首先，客户端向ZookeeperServer发起连接请求，此时状态为CONNECTING。</p>
<p>当连接建立好之后，Session状态转化为CONNECTED，此时可以进行数据的IO操作。 </p>
<p>如果Client和Server的连接出现丢失，则Client又会变成CONNECTING状态。</p>
<p>如果会话过期或者主动关闭连接时，此时连接状态为CLOSE。</p>
<p>如果是身份验证失败，直接结束。</p>
<p><img src="https://tu.dxysun.com/image-20210828173028504-20210828173029.png" alt="image-20210828173028504"></p>
<h1 id="ACL权限控制"><a href="#ACL权限控制" class="headerlink" title="ACL权限控制"></a>ACL权限控制</h1><p>Zookeeper作为一个分布式协调框架，内部存储了一些分布式系统运行时的状态的数据，比如master选举、比如分布式锁。</p>
<p>对这些数据的操作会直接影响到分布式系统的运行状态。因此，为了保证zookeeper中的数据的安全性，避免误操作带来的影响。Zookeeper提供了一套ACL权限控制机制来保证数据的安全。</p>
<p>使用<code>[scheme:id:permissions]</code>来表示acl权限</p>
<ul>
<li><p>scheme（权限模式）</p>
</li>
<li><p>标识授权策略ID（授权对象）</p>
</li>
<li><p>Permission：授予的权限</p>
</li>
</ul>
<p>ZooKeeper的权限控制是基于每个znode节点的，需要对每个节点设置权限，每个znode支持设置多种权限控制方案和多个权限，子节点不会继承父节点的权限，客户端无权访问某节点，但可能可以访问它的子节点。</p>
<p>ZK的节点有5种操作权限：CREATE、READ、WRITE、DELETE、ADMIN 也就是 增、删、改、查、管理权限，这5种权限简写为crwda(即：每个单词的首字符缩写)。</p>
<p>注：这5种权限中，delete是指对子节点的删除权限，其它4种权限指对自身节点的操作权限</p>
<p>身份的认证有4种方式：</p>
<ul>
<li>world：默认方式，相当于全世界都能访问</li>
<li>auth：代表已经认证通过的用户(cli中可以通过<code>addauth digest user:pwd</code>来添加当前上下文中的授权用户)</li>
<li>digest：即用户名:密码这种方式认证，这也是业务系统中最常用的</li>
<li>ip：使用Ip地址认证</li>
</ul>
<p><code>getAcl</code>获取某个节点的acl权限信息</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">getAcl path</span><br></pre></td></tr></table></figure>

<p><code>setAcl</code> 设置权限</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">setAcl path acl</span><br><span class="line">#例如</span><br><span class="line">setAcl path world:anyone:crwa #设置节点权限 crwa 不允许删除</span><br></pre></td></tr></table></figure>

<h1 id="Zookeeper可以解决那些实际问题"><a href="#Zookeeper可以解决那些实际问题" class="headerlink" title="Zookeeper可以解决那些实际问题"></a>Zookeeper可以解决那些实际问题</h1><h2 id="有序节点的使用场景"><a href="#有序节点的使用场景" class="headerlink" title="有序节点的使用场景"></a>有序节点的使用场景</h2><p>有序节点：全局ID</p>
<p>分布式锁</p>
<p>分布式队列</p>
<h2 id="同级节点的唯一性"><a href="#同级节点的唯一性" class="headerlink" title="同级节点的唯一性"></a>同级节点的唯一性</h2><p>master选举</p>
<p>分布式锁</p>
<h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://blog.csdn.net/dandandeshangni/article/details/80558383" target="_blank" rel="noopener">Zookeeper基础命令操作</a></p>
 
      <!-- reward -->
      
      <div id="reword-out">
        <div id="reward-btn">
          打赏
        </div>
      </div>
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
<div class="share-btn">
      <span class="share-sns share-outer">
        <i class="ri-share-forward-line"></i>
        分享
      </span>
      <div class="share-wrap">
        <i class="arrow"></i>
        <div class="share-icons">
          
          <a class="weibo share-sns" href="javascript:;" data-type="weibo">
            <i class="ri-weibo-fill"></i>
          </a>
          <a class="weixin share-sns wxFab" href="javascript:;" data-type="weixin">
            <i class="ri-wechat-fill"></i>
          </a>
          <a class="qq share-sns" href="javascript:;" data-type="qq">
            <i class="ri-qq-fill"></i>
          </a>
          <a class="douban share-sns" href="javascript:;" data-type="douban">
            <i class="ri-douban-line"></i>
          </a>
          <!-- <a class="qzone share-sns" href="javascript:;" data-type="qzone">
            <i class="icon icon-qzone"></i>
          </a> -->
          
          <a class="facebook share-sns" href="javascript:;" data-type="facebook">
            <i class="ri-facebook-circle-fill"></i>
          </a>
          <a class="twitter share-sns" href="javascript:;" data-type="twitter">
            <i class="ri-twitter-fill"></i>
          </a>
          <a class="google share-sns" href="javascript:;" data-type="google">
            <i class="ri-google-fill"></i>
          </a>
        </div>
      </div>
</div>

<div class="wx-share-modal">
    <a class="modal-close" href="javascript:;"><i class="ri-close-circle-line"></i></a>
    <p>扫一扫，分享到微信</p>
    <div class="wx-qrcode">
      <img src="//api.qrserver.com/v1/create-qr-code/?size=150x150&data=https://dxysun.com/2021/08/28/zookeeperForUse/" alt="微信分享二维码">
    </div>
</div>

<div id="share-mask"></div>  
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/java/" rel="tag">java</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/zookeeper/" rel="tag">zookeeper</a></li></ul>

    </footer>
  </div>

   
  <nav class="article-nav">
    
      <a href="/2021/08/28/zookeeperForPrinciple1/" class="article-nav-link">
        <strong class="article-nav-caption">上一篇</strong>
        <div class="article-nav-title">
          
            Zookeeper的架构设计及原理分析
          
        </div>
      </a>
    
    
      <a href="/2021/08/28/redisForPrinciple4/" class="article-nav-link">
        <strong class="article-nav-caption">下一篇</strong>
        <div class="article-nav-title">redis实战</div>
      </a>
    
  </nav>

  
   
  
</article>

</section>
      <footer class="footer">
  <div class="outer">
    <ul>
      <li>
        Copyrights &copy;
        2015-2024
        <i class="ri-heart-fill heart_icon"></i> dxysun
      </li>
    </ul>
    <ul>
      <li>
        
        
        
        由 <a href="https://hexo.io" target="_blank">Hexo</a> 强力驱动
        <span class="division">|</span>
        主题 - <a href="https://github.com/Shen-Yu/hexo-theme-ayer" target="_blank">Ayer</a>
        
      </li>
    </ul>
    <ul>
      <li>
        
        
        <span>
  <span><i class="ri-user-3-fill"></i>访问人数:<span id="busuanzi_value_site_uv"></span></s>
  <span class="division">|</span>
  <span><i class="ri-eye-fill"></i>浏览次数:<span id="busuanzi_value_page_pv"></span></span>
</span>
        
      </li>
    </ul>
    <ul>
      
        <li>
          <a href="https://beian.miit.gov.cn" target="_black" rel="nofollow">豫ICP备17012675号-1</a>
        </li>
        
    </ul>
    <ul>
      
    </ul>
    <ul>
      <li>
        <!-- cnzz统计 -->
        
      </li>
    </ul>
  </div>
</footer>
      <div class="float_btns">
        <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>

<div class="todark" id="todark">
  <i class="ri-moon-line"></i>
</div>

      </div>
    </main>
    <aside class="sidebar on">
      <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/"><img src="https://dxysun.com/static/logo.png" alt="迎着朝阳"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/archives">归档</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/categories">分类</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">标签</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/photos">相册</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/friends">友链</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/about">关于我</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="搜索">
        <i class="ri-search-line"></i>
      </a>
      
      
      <a class="nav-item-link" target="_blank" href="/atom.xml" title="RSS Feed">
        <i class="ri-rss-line"></i>
      </a>
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
    </aside>
    <script>
      if (window.matchMedia("(max-width: 768px)").matches) {
        document.querySelector('.content').classList.remove('on');
        document.querySelector('.sidebar').classList.remove('on');
      }
    </script>
    <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i>请我喝杯咖啡吧~</p>
  <div class="reward-box">
    
    <div class="reward-item">
      <img class="reward-img" src="https://tu.dxysun.com/alipay-20201219151322.jpg">
      <span class="reward-type">支付宝</span>
    </div>
    
    
    <div class="reward-item">
      <img class="reward-img" src="https://tu.dxysun.com/weixin-20201219151346.png">
      <span class="reward-type">微信</span>
    </div>
    
  </div>
</div>
    
<script src="/js/jquery-2.0.3.min.js"></script>


<script src="/js/lazyload.min.js"></script>

<!-- Tocbot -->


<script src="/js/tocbot.min.js"></script>

<script>
  tocbot.init({
    tocSelector: '.tocbot',
    contentSelector: '.article-entry',
    headingSelector: 'h1, h2, h3, h4, h5, h6',
    hasInnerContainers: true,
    scrollSmooth: true,
    scrollContainer: 'main',
    positionFixedSelector: '.tocbot',
    positionFixedClass: 'is-position-fixed',
    fixedSidebarOffset: 'auto'
  });
</script>

<script src="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.css">
<script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js"></script>

<script src="/dist/main.js"></script>

<!-- ImageViewer -->

<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.min.css">
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script>

<!-- MathJax -->

<script type="text/x-mathjax-config">
  MathJax.Hub.Config({
      tex2jax: {
          inlineMath: [ ['$','$'], ["\\(","\\)"]  ],
          processEscapes: true,
          skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
      }
  });

  MathJax.Hub.Queue(function() {
      var all = MathJax.Hub.getAllJax(), i;
      for(i=0; i < all.length; i += 1) {
          all[i].SourceElement().parentNode.className += ' has-jax';
      }
  });
</script>

<script src="https://cdn.jsdelivr.net/npm/mathjax@2.7.6/unpacked/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script>
  var ayerConfig = {
    mathjax: true
  }
</script>

<!-- Katex -->

<!-- busuanzi  -->


<script src="/js/busuanzi-2.3.pure.min.js"></script>


<!-- ClickLove -->

<!-- ClickBoom1 -->

<!-- ClickBoom2 -->

<!-- CodeCopy -->


<link rel="stylesheet" href="/css/clipboard.css">

<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
  function wait(callback, seconds) {
    var timelag = null;
    timelag = window.setTimeout(callback, seconds);
  }
  !function (e, t, a) {
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '<i class="ri-file-copy-2-line"></i><span>COPY</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      $(".article pre code").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });
      clipboard.on('success', function(e) {
        let $btn = $(e.trigger);
        $btn.addClass('copied');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-checkbox-circle-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPIED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-checkbox-circle-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
      clipboard.on('error', function(e) {
        e.clearSelection();
        let $btn = $(e.trigger);
        $btn.addClass('copy-failed');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-time-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPY FAILED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-time-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
    }
    initCopyCode();
  }(window, document);
</script>


<!-- CanvasBackground -->


<script src="/js/dz.js"></script>



    
  </div>
</body>

</html>